﻿'(c) Vasian Cepa 2005
' Version 2

' Eulates StrCmpLogicalW, but not fully

Friend Class StringLogicalComparer : Implements IComparer(Of String)
    Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
        'get rid of special cases
        If (x Is Nothing) AndAlso (y Is Nothing) Then
            Return 0
        ElseIf x Is Nothing Then
            Return -1
        ElseIf y Is Nothing Then
            Return 1
        End If

        If (x.Equals(String.Empty) AndAlso (y.Equals(String.Empty))) Then
            Return 0
        ElseIf x.Equals(String.Empty) Then
            Return -1
        ElseIf y.Equals(String.Empty) Then
            Return -1
        End If

        'WE style, special case
        Dim sp1 As Boolean = [Char].IsLetterOrDigit(x, 0)
        Dim sp2 As Boolean = [Char].IsLetterOrDigit(y, 0)
        If sp1 AndAlso Not sp2 Then
            Return 1
        End If
        If Not sp1 AndAlso sp2 Then
            Return -1
        End If

        Dim i1 As Integer = 0, i2 As Integer = 0
        'current index
        Dim r As Integer = 0
        ' temp result
        Do
            Dim c1 As Boolean = [Char].IsDigit(x, i1)
            Dim c2 As Boolean = [Char].IsDigit(y, i2)
            If Not c1 AndAlso Not c2 Then
                Dim letter1 As Boolean = [Char].IsLetter(x, i1)
                Dim letter2 As Boolean = [Char].IsLetter(y, i2)
                If (letter1 AndAlso letter2) OrElse (Not letter1 AndAlso Not letter2) Then
                    If letter1 AndAlso letter2 Then
                        r = [Char].ToLower(x(i1)).CompareTo([Char].ToLower(y(i2)))
                    Else
                        r = x(i1).CompareTo(y(i2))
                    End If
                    If r <> 0 Then
                        Return r
                    End If
                ElseIf Not letter1 AndAlso letter2 Then
                    Return -1
                ElseIf letter1 AndAlso Not letter2 Then
                    Return 1
                End If
            ElseIf c1 AndAlso c2 Then
                r = CompareNum(x, i1, y, i2)
                If r <> 0 Then
                    Return r
                End If
            ElseIf c1 Then
                Return -1
            ElseIf c2 Then
                Return 1
            End If
            i1 += 1
            i2 += 1
            If (i1 >= x.Length) AndAlso (i2 >= y.Length) Then
                Return 0
            ElseIf i1 >= x.Length Then
                Return -1
            ElseIf i2 >= y.Length Then
                Return -1
            End If
        Loop
    End Function

    Private Shared Function CompareNum(s1 As String, ByRef i1 As Integer, s2 As String, ByRef i2 As Integer) As Integer
        Dim nzStart1 As Integer = i1, nzStart2 As Integer = i2
        ' nz = non zero
        Dim end1 As Integer = i1, end2 As Integer = i2

        ScanNumEnd(s1, i1, end1, nzStart1)
        ScanNumEnd(s2, i2, end2, nzStart2)
        Dim start1 As Integer = i1
        i1 = end1 - 1
        Dim start2 As Integer = i2
        i2 = end2 - 1

        Dim nzLength1 As Integer = end1 - nzStart1
        Dim nzLength2 As Integer = end2 - nzStart2

        If nzLength1 < nzLength2 Then
            Return -1
        ElseIf nzLength1 > nzLength2 Then
            Return 1
        End If

        Dim j1 As Integer = nzStart1, j2 As Integer = nzStart2
        While j1 <= i1
            Dim r As Integer = s1(j1).CompareTo(s2(j2))
            If r <> 0 Then
                Return r
            End If
            j1 += 1
            j2 += 1
        End While
        ' the nz parts are equal
        Dim length1 As Integer = end1 - start1
        Dim length2 As Integer = end2 - start2
        If length1 = length2 Then
            Return 0
        End If
        If length1 > length2 Then
            Return -1
        End If
        Return 1
    End Function

    'lookahead
    Private Shared Sub ScanNumEnd(s As String, start As Integer, ByRef [end] As Integer, ByRef nzStart As Integer)
        nzStart = start
        [end] = start
        Dim countZeros As Boolean = True
        While [Char].IsDigit(s, [end])
            If countZeros AndAlso s([end]).Equals("0"c) Then
                nzStart += 1
            Else
                countZeros = False
            End If
            [end] += 1
            If [end] >= s.Length Then
                Exit While
            End If
        End While
    End Sub

End Class
'EOC